package com.ymttest.utils.compare;

import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;

import org.apache.commons.lang.StringUtils;
import org.json.JSONArray;
import org.json.JSONObject;

import com.google.gson.GsonBuilder;
import com.ymttest.utils.compare.parameter.IgnoreParam;
import com.ymttest.utils.compare.parameter.OrderCompare;
import com.ymttest.utils.compare.parameter.compareKey;

public class AssertHelper {
	private static final String OrderCompareKey = "OrderCompare";
	private static final String OrderCompareListValue = "OrderCompareListValue";

	public static LoggerInterface logger = new YmtLoggerAdapter();

	public static void setLoggerInterface(LoggerInterface loggerInterface) {
		logger = loggerInterface;
	}

	public static void assertTime(Date expectedDate, Date actualDate, String desc) {
		assertTime(expectedDate, actualDate, 120, desc);
	}

	public static void assertTime(Date expectedDate, Date actualDate, int timeDiffSecond, String desc) {
		logger.verifyNotNull(expectedDate, desc + ",期待时间不该为空");
		logger.verifyNotNull(actualDate, desc + ",实际时间不该为空");

		long time1 = expectedDate.getTime();
		long time2 = actualDate.getTime();
		logger.verifyEquals(true, Math.abs(time1 - time2) <= timeDiffSecond * 1000,
				desc + ", 时间比较,期望时间[" + expectedDate + "],实际时间[" + actualDate + "] 误差应不超过 " + timeDiffSecond + "秒");

	}

	public static <T> void assertResultEqual(T tgtBean, T actBean, String desc) {
		assertResultEqual(pojoToTreeMap(tgtBean), pojoToTreeMap(actBean), desc);

	}

	public static <T> void assertResultEqual(JSONObject tgtJsonObj, JSONObject actJsonObj, Class<T> clz, String desc) {
		T tgtBean = new GsonBuilder().create().fromJson(tgtJsonObj.toString(), clz);
		T actBean = new GsonBuilder().create().fromJson(actJsonObj.toString(), clz);
		assertResultEqual(tgtBean, actBean, desc);
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	public static void assertResultEqual(Map<String, Object> tgtRetMap, Map<String, Object> actRetMap, String desc) {
		logger.verifyNotNull(tgtRetMap, desc + ",tgtRetMap should not be null");
		logger.verifyNotNull(actRetMap, desc + ",actRetMap should not be null");

		actRetMap.keySet().forEach(ele -> {
			if (!tgtRetMap.containsKey(ele)) {
				logger.verifyEquals(true, false, desc + ", 实际值存在  key:[" + ele + "],而期望值中不存在该key");
			}
		});

		for (String actKey : tgtRetMap.keySet()) {
			if (actRetMap.containsKey(actKey)) {
				Object tgtNodeObj = tgtRetMap.get(actKey);
				Object actNodeObj = actRetMap.get(actKey);

				if (tgtNodeObj instanceof List) {
					// if (actNodeObj != null && actNodeObj instanceof List) {
					// checkArray((List<?>) tgtNodeObj, (List<?>) actNodeObj,
					// desc + ", key:[" + actKey + "]");
					// } else {
					// logger.verifyEquals(true, false,
					// desc + ", key:[" + actKey + "], target value: List
					// size("+((List)tgtNodeObj).size()+"), actual value: not
					// List");
					// }
					if (actNodeObj == null || actNodeObj.toString().equals("null")) {
						logger.verifyEquals(true, false, desc + ", key:[" + actKey + "], target value: List size("
								+ ((List) tgtNodeObj).size() + "), actual value: null");
					} else {
						if (tgtNodeObj == null || tgtNodeObj.toString().equals("null")) {
							logger.verifyEquals(true, false,
									desc + ", key:[" + actKey + "], target value:null, actual value:  List size("
											+ ((List) actNodeObj).size() + ")");
						} else {

							if (!(actNodeObj instanceof List)) {
								logger.verifyEquals(true, false,
										desc + ", key:[" + actKey + "], target value: List size("
												+ ((List) tgtNodeObj).size() + "), actual value: not List");

							} else {
								checkArray((List<?>) tgtNodeObj, (List<?>) actNodeObj, desc + ", key:[" + actKey + "]");
							}
						}
					}

				} else if (tgtNodeObj instanceof Map) {
					if (actNodeObj != null && actNodeObj instanceof Map) {

						Map<String, Object> tgtNodeMap = (Map<String, Object>) tgtNodeObj;
						Map<String, Object> actNodeMap = (Map<String, Object>) actNodeObj;

						// 顺序比对
						if (tgtNodeMap.containsKey(OrderCompareKey)) {
							if (tgtNodeMap.containsKey(OrderCompareListValue)) {
								List<?> tgtNodeOrderCompareListTmp =  (List<?>) tgtNodeMap.get(OrderCompareListValue);
								
								if (tgtNodeOrderCompareListTmp.size()>0 && tgtNodeOrderCompareListTmp.get(0) instanceof Map){
									List<Map<String, Object>> tgtNodeOrderCompareList =  (List<Map<String, Object>>) tgtNodeOrderCompareListTmp;
									List<Map<String,Object>> actNodeOrderCompareList =  (List<Map<String, Object>>) actNodeMap.get(OrderCompareListValue);
									if (tgtNodeOrderCompareList.size()!=actNodeOrderCompareList.size()){
										logger.verifyEquals(true, false,
												desc + ",key:[" + actKey + "],[order compare], 两个list 数组长度不一致 ");
									}else{
										for(int i=0;i<tgtNodeOrderCompareList.size();i++){
											assertResultEqual(tgtNodeOrderCompareList.get(i),actNodeOrderCompareList.get(i),desc+",key:[" + actKey + "],[order compare]");
										}
									}
								}else{
									checkArrayByOrder((List<?>) tgtNodeMap.get(OrderCompareListValue),
											   (List<?>) actNodeMap.get(OrderCompareListValue), 
											   desc + ", key:[" + actKey + "],[order compare] ");
								}
							}else{
								logger.verifyEquals(true, false,
										desc + ",key:[" + actKey + "],[order compare],Map has not key of OrderCompareListValue");
							}
						} else {
							assertResultEqual(tgtNodeMap, actNodeMap, desc + " parentkey:[" + actKey + "] ");
						}
					} else {
						logger.verifyEquals(true, false,
								desc + ", key:[" + actKey + "], target value: map, actual value: not Map");
					}
				} else {
					try {
						Object[] ret = checkWorkAround(tgtNodeObj, actNodeObj);
						if (ret[0] instanceof Date && ret[1] instanceof Date) {
							assertTime((Date) ret[0], (Date) ret[1], desc + ", key:[" + actKey + "],target value:["
									+ tgtNodeObj + "],actual value:[" + actNodeObj + "]");
						} else {
							logger.verifyEquals(ret[0], ret[1], desc + ", key:[" + actKey + "],target value:["
									+ tgtNodeObj + "],actual value:[" + actNodeObj + "]");
						}
					} catch (Exception e) {
						logger.failure(desc + ", key:[" + actKey + "],target value:[" + tgtNodeObj + "],actual value:["
								+ actNodeObj + "]", e);
					}
				}
			} else {
				logger.verifyEquals(true, false, desc + ", 期望值存在  key:[" + actKey + "],而实际值中不存在该key");
			}
		}
	}
	
	
	public static boolean checkArray(String[] array1, String[] array2, String desc) {
		try {
			Arrays.sort(array1);
			Arrays.sort(array2);

			StringBuffer a1 = new StringBuffer();
			for (int i = 0; i < array1.length; i++) {
				a1.append(array1[i] + ", ");
			}

			StringBuffer a2 = new StringBuffer();
			for (int i = 0; i < array2.length; i++) {
				a2.append(array2[i] + ", ");
			}

			boolean aa = Arrays.equals(array1, array2);
			if (aa == true) {
				logger.verifyEquals(true, aa, desc + ",两个数组中的值相同,[" + a1 + "],[" + a2 + "]");
			} else {
				logger.verifyEquals(true, aa, desc + ",两个数组中的值不相同,[" + a1 + "],[" + a2 + "]");
			}

			return aa;

		} catch (Exception e) {
			logger.failure(e.getMessage());
		}

		return false;
	}

	public static boolean checkArrayByOrder(String[] array1, String[] array2, String desc) {
		try {
			StringBuffer a1 = new StringBuffer();
			for (int i = 0; i < array1.length; i++) {
				a1.append(array1[i] + ", ");
			}

			StringBuffer a2 = new StringBuffer();
			for (int i = 0; i < array2.length; i++) {
				a2.append(array2[i] + ", ");
			}

			boolean aa = Arrays.equals(array1, array2);
			if (aa == true) {
				logger.verifyEquals(true, aa, desc + ",两个数组中的值相同,[" + a1 + "],[" + a2 + "]");
			} else {
				logger.verifyEquals(true, aa, desc + ",两个数组中的值不相同,[" + a1 + "],[" + a2 + "]");
			}

			return aa;

		} catch (Exception e) {
			logger.failure(e.getMessage());
		}

		return false;
	}

	public static boolean checkArray(List<?> strList1, List<?> strList2, String desc) {
		List<String> list1 = new ArrayList<>();
		List<String> list2 = new ArrayList<>();

		strList1.forEach(ele -> list1.add(String.valueOf(ele)));
		strList2.forEach(ele -> list2.add(String.valueOf(ele)));

		String[] strs1 = (String[]) list1.toArray(new String[0]);

		String[] strs2 = (String[]) list2.toArray(new String[0]);

		return checkArray(strs1, strs2, desc);

	}

	public static boolean checkArrayByOrder(List<?> strList1, List<?> strList2, String desc) {
		List<String> list1 = new LinkedList<>();
		List<String> list2 = new LinkedList<>();

		strList1.forEach(ele -> list1.add(String.valueOf(ele)));
		strList2.forEach(ele -> list2.add(String.valueOf(ele)));

		String[] strs1 = (String[]) list1.toArray(new String[0]);

		String[] strs2 = (String[]) list2.toArray(new String[0]);

		return checkArrayByOrder(strs1, strs2, desc);

	}

	/**
	 * 2个JSONArray转数组数组对比
	 * 
	 * @param strList1
	 * @param strList2
	 * @throws JSONException
	 * @throws Exception
	 */
	public static boolean checkArray(JSONArray strList1, JSONArray strList2, String desc) throws Exception {
		String[] strstring1 = new String[strList1.length()];
		String[] strstring2 = new String[strList2.length()];

		for (int i = 0; i < strList1.length(); i++) {
			strstring1[i] = strList1.getString(i);
		}

		for (int i = 0; i < strList2.length(); i++) {
			strstring2[i] = strList2.getString(i);
		}
		return checkArray(strstring1, strstring2, desc);

	}

	private static Object[] checkWorkAround(Object tgtNodeObj, Object actNodeObj) {

		// for number
		try {
			if (tgtNodeObj != null && actNodeObj != null) {
				// 整数，小数，负数
				Pattern pattern = Pattern.compile("\\d+\\.\\d+$|-\\d+\\.\\d+$|^\\d+$|-\\d+$");
				boolean tgtMathcPtnAndActMatchPth = (pattern.matcher(tgtNodeObj.toString()).find()
						&& pattern.matcher(actNodeObj.toString()).find());

				boolean tgtIsNotNumAndActIsNum = !(StringUtils.isNumeric(tgtNodeObj.toString()))
						&& (StringUtils.isNumeric(actNodeObj.toString()));

				boolean tgtIsNumAndActIsNotNum = (StringUtils.isNumeric(tgtNodeObj.toString()))
						&& !(StringUtils.isNumeric(actNodeObj.toString()));

				boolean containZimu = (Pattern.compile(".*[a-zA-Z]+.*").matcher(tgtNodeObj.toString()).find()
						|| Pattern.compile(".*[a-zA-Z]+.*").matcher(actNodeObj.toString()).find());

				boolean containHanzi = (Pattern.compile("([\u4e00-\u9fa5]+)").matcher(tgtNodeObj.toString()).find()
						|| Pattern.compile("([\u4e00-\u9fa5]+)").matcher(actNodeObj.toString()).find());

				boolean containSymbol = String.valueOf(tgtNodeObj).contains("-")
						|| String.valueOf(actNodeObj).contains("-");

				boolean isNumberic = StringUtils.isNumeric(String.valueOf(tgtNodeObj).replaceFirst("\\.", ""))
						&& StringUtils.isNumeric(String.valueOf(actNodeObj).replaceFirst("\\.", ""));

				if (isNumberic && !containSymbol && !containHanzi && !containZimu
						&& (tgtIsNotNumAndActIsNum || tgtIsNumAndActIsNotNum || tgtMathcPtnAndActMatchPth)) {
					tgtNodeObj = Double.parseDouble(tgtNodeObj.toString());
					actNodeObj = Double.parseDouble(actNodeObj.toString());
				}
			} else {
				// for null and "","null" ->""
				tgtNodeObj = (tgtNodeObj == null) ? "" : tgtNodeObj;
				actNodeObj = (actNodeObj == null) ? "" : actNodeObj;

			}
		} catch (NumberFormatException e) {
			logger.failure("checkWorkAround", e);
		}

		return new Object[] { tgtNodeObj, actNodeObj };

	}

	@SuppressWarnings({ "rawtypes", "unchecked" })
	private static <T> Map<String, Object> pojoToTreeMap(T t) {
		if (t == null) {
			return null;
		}
		Map<String, Object> ret = new HashMap<String, Object>();
		try {
			Field[] fs = t.getClass().getDeclaredFields();
			for (Field field : fs) {
				//字段是否忽略比较
				if (field.getAnnotation(IgnoreParam.class) == null) {
					field.setAccessible(true);
					if (field.get(t) instanceof List) {
						List tmp = (List) field.get(t);
						if (tmp.size() > 0) {
							if (isBaiscType(tmp.get(0))) {
								// 顺序比较
								if (field.getAnnotation(OrderCompare.class) != null) {
									Map<String, Object> tmpMap = new LinkedHashMap<>();
									tmpMap.put(OrderCompareListValue, tmp);
									tmpMap.put(OrderCompareKey, true);
									
									ret.put(field.getName(), tmpMap);

								} else {
									// 非顺序比较
									ret.put(field.getName(), tmp);
								}

							} else {
								//List 非基础对象  顺序比对不需要comparekey，且会忽略comparekey。 非顺序比较需要compareKey
								Map<String, Object> tmpMap = new LinkedHashMap<>();
								// 顺序比较
								if (field.getAnnotation(OrderCompare.class) != null) {
									List<Map<String, Object>> tmpMapList = new LinkedList<>();
									tmp.forEach(x->tmpMapList.add(pojoToTreeMap(x)));
									tmpMap.put(OrderCompareListValue, tmpMapList);
									tmpMap.put(OrderCompareKey, true);
									
								} else {
									// 非顺序比较
									Field key = getKey(tmp.get(0));
									key.setAccessible(true);
									for (int i = 0; i < tmp.size(); i++) {
										tmpMap.put(String.valueOf(key.get(tmp.get(i))), pojoToTreeMap(tmp.get(i)));
									}
								}

								ret.put(field.getName(), tmpMap);

							}
						} else {
							ret.put(field.getName(), new ArrayList<>());
						}

					} else {
						if (isBaiscType(field.get(t))) {
							ret.put(field.getName(), field.get(t));
						} else {
							ret.put(field.getName(), pojoToTreeMap(field.get(t)));
						}

					}
				}
			}
		} catch (Exception e) {
			logger.failure(e.getMessage());
		}

		return ret;

	}

	private static <T> boolean isBaiscType(T t) {
		return t instanceof String || t instanceof Integer || t instanceof Double || t instanceof Float
				|| t instanceof Short || t instanceof Boolean || t instanceof Byte || t instanceof Long
				|| t instanceof Date || t instanceof BigDecimal;

	}

	private static <T> Field getKey(T t) throws Exception {
		/*
		 * Class<?> clazz = t.getClass(); Field[] fields =
		 * clazz.getDeclaredFields(); for (Field field : fields) { if
		 * (field.getAnnotation(compareKey.class) != null) return field; } throw
		 * new Exception("对象["+ t.getClass().getName()+"],没有主键compareKey") ;
		 */

		Field filed = findKey(t);
		if (filed == null) {
			throw new Exception("对象[" + t.getClass().getName() + "],没有主键compareKey");
		} else {
			return filed;
		}

	}

	private static <T> Field findKey(T t) {
		Class<?> clazz = t.getClass();
		Field[] fields = clazz.getDeclaredFields();
		for (Field field : fields) {
			if (field.getAnnotation(compareKey.class) != null)
				return field;
		}
		return null;

	}

	public static void main(String args[]) {

		checkWorkAround("123,123", "123.33");
	}

}
